package cii_lib;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CCII_ACCESS_INTERF extends CCII_TABLE_INDEX{
	
	private static final Logger log = LoggerFactory.getLogger(CCII_ACCESS_INTERF.class);
	
  private CCII_INTERFACE _interf;
  private CCII_COMPONENT _comp;
  public CCII_ACCESS_INTERF(CCII_TABLE_INDEX table, CCII_INTERFACE interf, CCII_COMPONENT comp) {
    super(table,1);
    _interf=interf;
    _comp=comp;
    if (
	GetItem().CompID!=GetCompID()
	|| GetItem().Name==null || GetItem().Repeat<0
	|| (GetItem().Type!=CIIlib.TABLE_TYPE.AREA && GetItem().Type!=CIIlib.TABLE_TYPE.WORD &&  GetItem().Type!=CIIlib.TABLE_TYPE.BITS && GetItem().Type!=CIIlib.TABLE_TYPE.GLUE)
	|| GetItem().Repeat==0 && (GetItem().Width!=0 || GetItem().Number!=0 || GetItem().Address!=-1)
	|| GetItem().Repeat>0 && (GetItem().Width<1 || GetItem().Number<1 || GetItem().Access==CIIlib.TABLE_ACCESS.NA || GetItem().Address<0 || GetItem().Position<0)
	|| GetItem().ParIntVal!=-1
	|| (GetItem().Access==CIIlib.TABLE_ACCESS.RC && GetItem().ParStrVal==null)
	|| (GetItem().Access!=CIIlib.TABLE_ACCESS.RC && GetItem().ParStrVal!=null)
    )
      try {
	throw new CiiLibException("CCII_ACCESS_INTERF",CiiLibException.MSG.TABLE_ERROR);
      } catch (CiiLibException e) {log.error("", e);}
  }
  public boolean Exist() {return((GetItem()==null || GetItem().Number==0)?false:true);};
  public String Name()   {return(GetItem().Name);};
  public String PathName()   {return(CIIlib.PathCompName(_comp)+"."+GetItem().Name);};
  public int Width()     {return(GetItem().Width);};
  public int Number()    {return(GetItem().Number);};
  public int Repeat()    {return(GetItem().Repeat);};

  public CIIdata Read(int num, int grp, CIIdata data) {
    if (data==null) data = new CIIdata(GetItem().Width);
    int index=grp*GetItem().Repeat+num;
    int range=GetItem().Number*GetItem().Repeat;
    if (num<0 || grp<0 || index>=range) try {
      throw new CiiLibException("CCII_ACCESS_INTERF",CiiLibException.MSG.INDEX_ERROR);
    } catch (CiiLibException e) {log.error("", e);}
    //TODO zmiana generacji
    if (GetItem().Access==CIIlib.TABLE_ACCESS.WO || GetItem().Access==CIIlib.TABLE_ACCESS.NA) 
    	try {
    		throw new CiiLibException("Accessing: "+GetItem().Access,CiiLibException.MSG.ACCESS_ERROR);
    	} catch (CiiLibException e) {log.error("", e);}
    //
    data.WidthSet(GetItem().Width);
    if      (GetItem().Type==CIIlib.TABLE_TYPE.AREA) return(CII_AREA_Read(num,grp,data));
    else if (GetItem().Type==CIIlib.TABLE_TYPE.WORD) return(CII_WORD_Read(num,grp,data));
    else if (GetItem().Type==CIIlib.TABLE_TYPE.BITS) return(CII_BITS_Read(num,grp,data));    
    try {
      throw new CiiLibException("CCII_ACCESS_INTERF",CiiLibException.MSG.INDEX_ERROR);
    } catch (CiiLibException e) {log.error("", e);}
    return(null);
  };
  //TODO do generacji
  public long readSimple() {
	  CIIdata tmp = new CIIdata(this.Width());
	  this.Read(0, 0, tmp);
	  return tmp.GetValue();
  }
  
  public void Write(int num, int grp, CIIdata data) {
    int index=grp*GetItem().Repeat+num;
    int range=GetItem().Number*GetItem().Repeat;
    if (num<0 || grp<0 || index>=range) try {
      throw new CiiLibException("CCII_ACCESS_INTERF",CiiLibException.MSG.INDEX_ERROR);
    } catch (CiiLibException e) {log.error("", e);}
    //TODO zmiana generacji
    if (GetItem().Access==CIIlib.TABLE_ACCESS.NA || GetItem().Access==CIIlib.TABLE_ACCESS.RO ||
    		GetItem().Access==CIIlib.TABLE_ACCESS.RC) 
    	try {
    		throw new CiiLibException("Accessing: "+GetItem().Access,CiiLibException.MSG.ACCESS_ERROR);
    	} catch (CiiLibException e) {log.error("", e);}
    //
    if      (GetItem().Type==CIIlib.TABLE_TYPE.AREA) CII_AREA_Write(num,grp,data);
    else if (GetItem().Type==CIIlib.TABLE_TYPE.WORD) CII_WORD_Write(num,grp,data);
    else if (GetItem().Type==CIIlib.TABLE_TYPE.BITS) CII_BITS_Write(num,grp,data);
  };
//TODO do generacji
  public void writeSimple(long val) {
	  CIIdata tmp = new CIIdata(this.Width(),val);
	  this.Write(0, 0, tmp);
  } 
  public boolean isWritable() {
	  if (GetItem().Access==CIIlib.TABLE_ACCESS.NA || GetItem().Access==CIIlib.TABLE_ACCESS.RO ||
	    		GetItem().Access==CIIlib.TABLE_ACCESS.RC) 
	    	{
	    		log.debug("[CCII_ACCESS_INTERF] Accessing: "+GetItem().Access + " "+ CiiLibException.MSG.ACCESS_ERROR);
	    		return false;
	    	} 
	  else return true;
  }
  
  public boolean SaveWrite(int num, int grp, CIIdata data) {
	    int index=grp*GetItem().Repeat+num;
	    int range=GetItem().Number*GetItem().Repeat;
	    if (num<0 || grp<0 || index>=range) {
	      log.debug("[CCII_ACCESS_INTERF] "+CiiLibException.MSG.INDEX_ERROR);
	      return false;
	    } 
	    //TODO zmiana generacji
	    if (GetItem().Access==CIIlib.TABLE_ACCESS.NA || GetItem().Access==CIIlib.TABLE_ACCESS.RO ||
	    		GetItem().Access==CIIlib.TABLE_ACCESS.RC) 
	    	{
	    		log.debug("[CCII_ACCESS_INTERF] Accessing: "+GetItem().Access + " "+ CiiLibException.MSG.ACCESS_ERROR);
	    		return false;
	    	} 
	    //
	    if      (GetItem().Type==CIIlib.TABLE_TYPE.AREA) CII_AREA_Write(num,grp,data);
	    else if (GetItem().Type==CIIlib.TABLE_TYPE.WORD) CII_WORD_Write(num,grp,data);
	    else if (GetItem().Type==CIIlib.TABLE_TYPE.BITS) CII_BITS_Write(num,grp,data);
	    return true;
	  };

  private void CII_AREA_Write(int num_pos, int grp_pos, CIIdata data){
    int PartNum = (GetItem().Width-1)/_comp.DataSize()+1;
    int LastWidth = GetItem().Width-(GetItem().Width/_comp.DataSize())*_comp.DataSize();
    int AreaWidth=_AreaSize(GetItem().Number);
    long PartAddr = GetItem().Address + grp_pos*AreaWidth*PartNum + num_pos;
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0)
	_interf.HardwareWrite(PartAddr+index*AreaWidth,data.GetValue(index*_comp.DataSize(),_comp.DataSize()));
      else
	_interf.HardwareWrite(PartAddr+index*AreaWidth,data.GetValue(index*_comp.DataSize(),LastWidth));
  }

  private CIIdata CII_AREA_Read(int num_pos, int grp_pos, CIIdata data) {
    int PartNum = (GetItem().Width-1)/_comp.DataSize()+1;
    int LastWidth = GetItem().Width-(GetItem().Width/_comp.DataSize())*_comp.DataSize();
    int AreaWidth=_AreaSize(GetItem().Number);
    long PartAddr = GetItem().Address + grp_pos*AreaWidth*PartNum + num_pos;
    long Data;
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0){
	Data=_interf.HardwareRead(PartAddr+index*AreaWidth);
	data.SetValue(index*_comp.DataSize(),_comp.DataSize(),Data);
      }
      else{
	Data=_interf.HardwareRead(PartAddr+index*AreaWidth);
	data.SetValue(index*_comp.DataSize(),LastWidth,Data);
      }
    return(data);
  }

  private void CII_WORD_Write(int num_pos, int grp_pos, CIIdata data){
    int PartNum = (GetItem().Width-1)/_comp.DataSize()+1;
    int LastWidth = GetItem().Width-(GetItem().Width/_comp.DataSize())*_comp.DataSize();
    long PartAddr = GetItem().Address + PartNum*(grp_pos*GetItem().Number + num_pos);
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0)
	_interf.HardwareWrite(PartAddr+index,data.GetValue(index*_comp.DataSize(),_comp.DataSize()));
      else
	_interf.HardwareWrite(PartAddr+index,data.GetValue(index*_comp.DataSize(),LastWidth));
  }

  private CIIdata CII_WORD_Read(int num_pos, int grp_pos, CIIdata data) {
    int PartNum = (GetItem().Width-1)/_comp.DataSize()+1;
    int LastWidth = GetItem().Width-(GetItem().Width/_comp.DataSize())*_comp.DataSize();
    long PartAddr = GetItem().Address + PartNum*(grp_pos*GetItem().Number + num_pos);
    long Data;
    for(int index=0; index<PartNum; index++)
      if (index+1<PartNum || LastWidth==0){
	Data=_interf.HardwareRead(PartAddr+index);
	data.SetValue(index*_comp.DataSize(),_comp.DataSize(),Data);
      }
      else{
	Data=_interf.HardwareRead(PartAddr+index);
	data.SetValue(index*_comp.DataSize(),LastWidth,Data);
      }
    return(data);
  }

  private void CII_BITS_Write(int num_pos, int grp_pos, CIIdata data) {
    int Pos = (grp_pos*GetItem().Number + num_pos)*GetItem().Width+GetItem().Position;
    long Res = _interf.HardwareRead(GetItem().Address);
	  log.debug("[CCII_ACCESS_INTERF] data " + Res);
    Res = (Res & (~(((1<<GetItem().Width)-1)<<Pos))) | (data.GetValue()<<Pos);
    _interf.HardwareWrite(GetItem().Address,Res);
  }

  private CIIdata CII_BITS_Read(int num_pos, int grp_pos, CIIdata data) {
    int Pos = (grp_pos*GetItem().Number + num_pos)*GetItem().Width+GetItem().Position;
    long Val = _interf.HardwareRead(GetItem().Address);
    Val = (Val>>Pos) & ((1<<GetItem().Width)-1);
    data.SetValue(0,GetItem().Width,Val);
    return(data);
  }
  
  private int _AreaSize(int num){
    if ((num--)==0) return(0);
    int cnt=1;
    while(num>1){
      num/=2;
      cnt++;
    }
    return(1<<cnt);
  }

  protected void Write(int num, int grp, long val){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetValue(val);
    Write(num,grp,Data);
  }

  protected void Write(int num, int grp, boolean val){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetValue(val);
    Write(num,grp,Data);
  }

  protected void Write(int num, int grp, String val){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetUni(val);
    Write(num,grp,Data);
  }

  protected void WriteBin(int num, int grp, String val){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetBin(val);
    Write(num,grp,Data);
  }

  protected void WriteDec(int num, int grp, String val){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetDec(val);
    Write(num,grp,Data);
  }

  protected void WriteHex(int num, int grp, String val){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetHex(val);
    Write(num,grp,Data);
  }

  protected void WriteStr(int num, int grp, String val){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetStr(val);
    Write(num,grp,Data);
  }

  protected void WriteHigh(int num, int grp){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetHigh();
    Write(num,grp,Data);
  }

  protected void WriteLow(int num, int grp){
    CIIdata Data = new CIIdata(GetItem().Width);
    Data.SetLow();
    Write(num,grp,Data);
  }
  /**
   * @author pdrabik 
   * @return Hardware interface using by this interface
   */
  public CCII_INTERFACE getHardwareInterface() {
	  return _interf;
  }

};
